package com.framework.core.mybatis.util;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import com.framework.core.mybatis.config.AutoField;
import com.framework.core.mybatis.context.TableInfo;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.JdbcType;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ObjectUtils;

import javax.persistence.Transient;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
 * 扫描包， 读取配置
 * created by zzg
 * 2024-12-06
 */
@Slf4j
public class ProcessUtil {
    public static List<Class<?>> scanPackage(String packageName) {
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
        provider.addIncludeFilter(new AnnotationTypeFilter(TableName.class));
        List<Class<?>> classes = new ArrayList<>();
        Set<BeanDefinition> components = provider.findCandidateComponents(packageName);
        for (BeanDefinition component : components) {
            try {
                classes.add(Class.forName(component.getBeanClassName()));
            } catch (ClassNotFoundException e) {
                log.error(e.getMessage(), e);
            }
        }
        return classes;
    }

    public static void convert2TableInfo(List<Class<?>> classList, List<TableInfo> tableInfoList) {
        for (Class<?> clazz : classList) {
            TableName tableName = clazz.getAnnotation(TableName.class);
            if (ObjectUtils.isEmpty(tableName)) {
                continue;
            }
            TableInfo tableInfo = TableInfo.builder()
                    .tableName(tableName.value()).build();
            List<Field> fields = FieldUtils.getAllFields(clazz);

            for (Field field : fields) {
                Transient transientAnnotation = field.getAnnotation(Transient.class);
                if (!ObjectUtils.isEmpty(transientAnnotation)) {
                    continue;
                }

                AutoField fieldAnnotation = field.getAnnotation(AutoField.class);
                String fieldName = null;
                String property = null;
                // 加一个jdbcType便于后续扩展和更改
                JdbcType jdbcType = JdbcType.VARCHAR;
                if (!ObjectUtils.isEmpty(fieldAnnotation)) {
                    fieldName = fieldAnnotation.field();
                    property = fieldAnnotation.value();
                    jdbcType = fieldAnnotation.jdbcType();
                }
                if (ObjectUtils.isEmpty(fieldName)) {
                    fieldName = FieldUtils.fromCamelCase(field.getName());
                }

                // 为空根据javaType设置默认值
                if (StringUtils.isBlank(property)) {
                    Class<?> fieldType = field.getType();
                    jdbcType = getJdbcTypeByFieldType(fieldType);
                }

                String comment = StringUtils.EMPTY;
                boolean isPrimaryKey = false;
                TableId tableIdAnnotation = field.getAnnotation(TableId.class);
                if (!ObjectUtils.isEmpty(tableIdAnnotation)) {
                    isPrimaryKey = true;
                    jdbcType = getDefaultPrimaryKeyJdbcType();
                    comment = "主键编号";
                }

                // 如果没有再加注释，直接从ApiModelProperty里面获取
                if (!ObjectUtils.isEmpty(property) && !property.toLowerCase(Locale.ROOT).contains(" COMMENT ")) {
                    ApiModelProperty commentAnnotation = field.getAnnotation(ApiModelProperty.class);
                    if (!ObjectUtils.isEmpty(commentAnnotation)) {
                        comment = commentAnnotation.value();
                    }
                }

                boolean isVersion = false;
                Version versionAnnotation = field.getAnnotation(Version.class);
                if (!ObjectUtils.isEmpty(versionAnnotation)) {
                    isVersion = true;
                    comment = "版本";
                    jdbcType = JdbcType.INTEGER;
                }

                tableInfo.addField(fieldName, property, comment, isPrimaryKey, isVersion, jdbcType);
            }

            tableInfoList.add(tableInfo);
        }
    }

    private static JdbcType getDefaultPrimaryKeyJdbcType() {
        return JdbcType.VARCHAR;
    }

    private static JdbcType getJdbcTypeByFieldType(Class<?> javaType) {
        JdbcType result = JdbcType.VARCHAR;
        if (javaType == null) {
            result = JdbcType.NULL;
        }
        if (javaType == String.class) {
            result = JdbcType.VARCHAR;
        } else if (javaType == Integer.class || javaType == int.class) {
            result = JdbcType.INTEGER;
        } else if (javaType == Boolean.class || javaType == boolean.class) {
            result = JdbcType.BOOLEAN;
        } else if (javaType == Byte.class || javaType == byte.class) {
            result = JdbcType.TINYINT;
        } else if (javaType == Short.class || javaType == short.class) {
            result = JdbcType.SMALLINT;
        } else if (javaType == Long.class || javaType == long.class) {
            result = JdbcType.BIGINT;
        } else if (javaType == Float.class || javaType == float.class) {
            result = JdbcType.FLOAT;
        } else if (javaType == Double.class || javaType == double.class) {
            result = JdbcType.DOUBLE;
        } else if (javaType == java.math.BigDecimal.class) {
            result = JdbcType.DECIMAL;
        } else if (javaType == java.sql.Date.class) {
            result = JdbcType.DATE;
        } else if (javaType == java.sql.Time.class) {
            result = JdbcType.TIME;
        } else if (javaType == java.sql.Timestamp.class) {
            result = JdbcType.TIMESTAMP;
        } else if (javaType == java.sql.Clob.class) {
            result = JdbcType.CLOB;
        } else if (javaType == java.sql.Blob.class) {
            result = JdbcType.BLOB;
        } else if (javaType.isArray() && javaType.getComponentType() == byte.class) {
            result = JdbcType.BLOB;
        }

        return result;
    }
}
